MOOC Utils

Utils like auto assignment solver extension and course notice announcment notification for MOOC courses

Shared UI components

Introduction#

shadcn/ui-based primitives used across the site: Button, Input, Card, Dialog, and the rest. Variants via CVA and Tailwind.

Project structure#

The UI components live under website/components/ui and are composed :

  • Base primitives from @base-ui/react
  • Shadcn/ui configuration via components.json
  • Tailwind CSS v4 with CSS Variables and design tokens defined in app/globals.css
  • Utility class merging via lib/utils.ts (clsx + tailwind-merge)

Core components#

This section summarizes the primary components and their responsibilities, focusing on props, variants, sizes, and styling hooks.

  • Button

  • Props: className, variant, size, plus primitive props

  • Variants: default, outline, secondary, ghost, destructive, link

  • Sizes: default, xs, sm, lg, icon, icon-xs, icon-sm, icon-lg

  • Accessibility: focus-visible ring, aria-invalid states, outline-none

  • Composition: integrates with Icon via SVG sizing rules

  • Input

  • Props: className, type, plus native input props

  • Accessibility: focus-visible ring, aria-invalid states, placeholder color

  • Composition: used inside InputGroup

  • Card

  • Props: className, size (“default” | “sm”)

  • Subcomponents: CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter

  • Composition: grid layout with optional action area

  • Dialog

  • Props: Root, Trigger, Portal, Overlay, Content (showCloseButton), Header, Footer, Title, Description

  • Accessibility: Backdrop, Close button with sr-only label, portal rendering

  • Composition: Uses Button for close, Lucide X icon

  • Alert Dialog

  • Props: Root, Trigger, Portal, Overlay, Content (size), Header, Footer, Media, Title, Description, Action, Cancel

  • Accessibility: Backdrop, portal rendering, size variants

  • Composition: Uses Button for actions

  • Badge

  • Props: className, variant, render, plus primitive props

  • Variants: default, secondary, destructive, outline, ghost, link

  • Accessibility: focus-visible ring, aria-invalid states

  • Combobox

  • Props: Root, Value, Trigger, Clear, Input (showTrigger, showClear), Content (positioning), List, Item, Group, Label, Collection, Empty, Separator, Chips, Chip, ChipsInput

  • Accessibility: keyboard navigation, highlight states, item indicators

  • Composition: integrates with InputGroup, Button, Lucide icons

  • Dropdown Menu

  • Props: Root, Portal, Trigger, Content (positioning), Group, Label (inset), Item (inset, variant), Submenu, Checkbox/Radio Items, Separator, Shortcut

  • Accessibility: keyboard navigation, submenus, indicators

  • Composition: integrates with Lucide icons

  • Field

  • Props: FieldSet, FieldLegend (variant), FieldGroup, Field (orientation), FieldContent, FieldLabel, FieldTitle, FieldDescription, FieldSeparator, FieldError (errors)

  • Accessibility: grouping roles, legends, separators, error roles

  • Composition: integrates with Label, Separator

  • Input Group

  • Props: InputGroup, InputGroupAddon (align), InputGroupButton (size, variant), InputGroupText, InputGroupInput, InputGroupTextarea

  • Accessibility: focus management, click-to-focus on addon

  • Composition: composes Button and Input/Textarea

  • Label

  • Props: className, plus native label props

  • Accessibility: associated with controls via htmlFor

  • Select

  • Props: Root, Trigger, Portal, Content (positioning), Group, Label, Item, Separator, Value

  • Accessibility: keyboard navigation, highlight states

  • Composition: integrates with Lucide icons

  • Separator

  • Props: className, decorative, orientation

  • Accessibility: semantic role via role attribute

  • Sonner

  • Props: toast manager and helpers

  • Integration: toast notifications

  • Textarea

  • Props: className, plus native textarea props

  • Accessibility: focus-visible ring, aria-invalid states

  • Composition: used inside InputGroup

Architecture overview#

The UI components follow a layered architecture:

  • Primitive wrappers: thin wrappers around @base-ui/react primitives
  • Styling layer: class merging via cn(), variants via class-variance-authority
  • Theme layer: Tailwind CSS v4 with CSS variables mapped to design tokens
  • Composition layer: components compose smaller building blocks (e.g., Button, Input, Label)

Detailed component analysis#

Button#

  • Props interface
  • className: string
  • variant: “default” | “outline” | “secondary” | “ghost” | “destructive” | “link”
  • size: “default” | “xs” | “sm” | “lg” | “icon” | “icon-xs” | “icon-sm” | “icon-lg”
  • Additional primitive props
  • Styling customization
  • Uses cva() for variants and sizes
  • Focus-visible ring, aria-invalid ring, disabled opacity, icon sizing rules
  • Accessibility
  • Focus-visible ring, outline-none, aria-invalid states
  • Usage examples
  • Primary action: variant=“default”, size=“default”
  • Icon-only: variant=“ghost”, size=“icon”
  • Danger: variant=“destructive”

Input#

  • Props interface
  • className: string
  • type: string
  • Additional native input props
  • Styling customization
  • Focus-visible ring, aria-invalid ring, placeholder color, disabled states
  • Accessibility
  • Focus-visible ring, aria-invalid states
  • Usage examples
  • Inside InputGroup for addons/buttons
  • Controlled via form libraries

Card#

  • Props interface
  • className: string
  • size: “default” | “sm”
  • Subcomponents
  • CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter
  • Styling customization
  • Ring, background, shadow, rounded corners, grid layout
  • Accessibility
  • Semantic grouping via data-* attributes and slots
  • Usage examples
  • Feature cards with action buttons
  • Content containers with optional descriptions

Dialog#

  • Props interface
  • Dialog: Root
  • DialogTrigger: Trigger
  • DialogPortal: Portal
  • DialogOverlay: Backdrop
  • DialogContent: Popup (showCloseButton?)
  • DialogHeader/Footer: container
  • DialogTitle/Description: Title/Description
  • Styling customization
  • Animation classes, overlay backdrop, fixed positioning, z-index
  • Accessibility
  • Portal rendering, sr-only close label, backdrop filtering support
  • Usage examples
  • Form modals with header/footer
  • Confirmation dialogs with close button

Alert dialog#

  • Props interface
  • AlertDialog: Root
  • AlertDialogTrigger: Trigger
  • AlertDialogPortal: Portal
  • AlertDialogOverlay: Backdrop
  • AlertDialogContent: Popup (size?)
  • AlertDialogHeader/Footer: container
  • AlertDialogMedia: media area
  • AlertDialogTitle/Description: Title/Description
  • AlertDialogAction: Button
  • AlertDialogCancel: Close with Button renderer
  • Styling customization
  • Size variants, grid layout, animation classes
  • Accessibility
  • Portal rendering, size-aware layouts
  • Usage examples
  • Confirmation prompts with destructive action
  • Informational alerts with media

Badge#

  • Props interface
  • className: string
  • variant: “default” | “secondary” | “destructive” | “outline” | “ghost” | “link”
  • render: component renderer
  • Additional primitive props
  • Styling customization
  • Variants via cva(), focus-visible ring, aria-invalid states
  • Accessibility
  • Focus-visible ring, aria-invalid states
  • Usage examples
  • Status badges, tags, links

Combobox#

  • Props interface
  • Combobox: Root
  • ComboboxValue: Value
  • ComboboxTrigger: Trigger (with ChevronDown)
  • ComboboxClear: Clear (with X)
  • ComboboxInput: Input (showTrigger?, showClear?, disabled?)
  • ComboboxContent: Popup (positioning props)
  • ComboboxList: List
  • ComboboxItem: Item (with Check indicator)
  • ComboboxGroup/Label: Grouping
  • ComboboxCollection/Empty/Separator
  • ComboboxChips/Chip/ChipsInput
  • Styling customization
  • Positioner-based placement, chips mode, item highlighting
  • Accessibility
  • Keyboard navigation, highlight states, item indicators
  • Usage examples
  • Single/multi-select inputs, searchable lists, chips selection
  • Props interface
  • DropdownMenu: Root
  • DropdownMenuPortal: Portal
  • DropdownMenuTrigger: Trigger
  • DropdownMenuContent: Popup (positioning)
  • DropdownMenuGroup/Label (inset)
  • DropdownMenuItem (inset, variant)
  • DropdownMenuSubmenu: SubRoot/SubTrigger/SubContent
  • DropdownMenuCheckboxItem/RadioItem
  • DropdownMenuSeparator
  • DropdownMenuShortcut
  • Styling customization
  • Positioner-based placement, nested submenus, variant colors
  • Accessibility
  • Keyboard navigation, submenus, indicators
  • Usage examples
  • Context menus, navigation dropdowns, settings panels

Field#

  • Props interface
  • FieldSet, FieldLegend (variant), FieldGroup
  • Field (orientation: “vertical” | “horizontal” | “responsive”)
  • FieldContent, FieldLabel, FieldTitle, FieldDescription, FieldSeparator, FieldError (errors)
  • Styling customization
  • Orientation-dependent layout, responsive breakpoints, separators
  • Accessibility
  • Role grouping, legends, separators, error roles
  • Usage examples
  • Form groups with labels, descriptions, and error lists

Input group#

  • Props interface
  • InputGroup
  • InputGroupAddon (align: “inline-start” | “inline-end” | “block-start” | “block-end”)
  • InputGroupButton (size variants, variant)
  • InputGroupText
  • InputGroupInput, InputGroupTextarea
  • Styling customization
  • Border focus ring, alignment, spacing, disabled opacity
  • Accessibility
  • Click-to-focus on addon, proper focus order
  • Usage examples
  • Inputs with prepend/append buttons or text
  • Search bars with action buttons

Label#

  • Props interface
  • className: string
  • Additional native label props
  • Styling customization
  • Inherits from base label styles
  • Accessibility
  • Associated with controls via htmlFor
  • Usage examples
  • Labels for inputs, checkboxes, radios

Select#

  • Props interface
  • Root, Trigger, Portal, Content (positioning), Group, Label, Item, Separator, Value
  • Styling customization
  • Positioner-based placement, item highlighting
  • Accessibility
  • Keyboard navigation, highlight states
  • Usage examples
  • Dropdown selects, option pickers

Separator#

  • Props interface
  • className: string
  • decorative: boolean
  • orientation: “horizontal” | “vertical”
  • Styling customization
  • Border color, margin, padding
  • Accessibility
  • role attribute for semantic separation
  • Usage examples
  • Dividers in forms, menus, cards

Sonner#

  • Props interface
  • Toast manager and helpers
  • Styling customization
  • Notification styling via Tailwind classes
  • Usage examples
  • Global notifications, toasts

Textarea#

  • Props interface
  • className: string
  • Additional native textarea props
  • Styling customization
  • Focus-visible ring, aria-invalid ring, disabled states
  • Accessibility
  • Focus-visible ring, aria-invalid states
  • Usage examples
  • Multi-line inputs, comments, descriptions

Dependency analysis#

The components depend on:

  • Base primitives (@base-ui/react/*)
  • Styling utilities (class-variance-authority, clsx, tailwind-merge)
  • Theme (Tailwind CSS v4, CSS variables, shadcn/tailwind.css)
  • Icons (lucide-react)

Performance considerations#

  • Prefer variant and size props over ad-hoc className overrides to keep styles predictable and efficient.
  • Use InputGroup to avoid redundant borders and shadows on composite inputs.
  • Use CSS variables for theme updates without rebuilding styles.
  • Keep animations minimal; Dialog and AlertDialog use lightweight transitions.
  • Avoid excessive nesting in Field and DropdownMenu to reduce DOM traversal.

Troubleshooting guide#

  • Focus ring not visible
  • Ensure focus-visible ring classes are applied and not overridden.
  • Verify theme variables for ring color.
  • Disabled state not working
  • Check disabled pointer-events and opacity classes.
  • Invalid state styling inconsistent
  • Confirm aria-invalid classes and destructives ring/opacity.
  • Combobox menu misaligned
  • Adjust positioner props (side, align, offsets) and ensure anchor is set.
  • Dropdown submenu not opening
  • Verify SubmenuRoot and SubTrigger are paired and not disabled.
  • InputGroup addon click does nothing
  • Confirm click handler focuses the input element.

Conclusion#

CVA variants plus Tailwind. Add a variant before forking a component.

Appendices#

  • Theme integration
  • Design tokens defined in app/globals.css map to Tailwind variables.
  • components.json configures shadcn/ui style, icon library, and CSS variables.
  • Practical form validation patterns
  • Use Field components to group labels, descriptions, and errors.
  • Apply aria-invalid on inputs and observe destructive ring/opacity.
  • Use InputGroup for prepend/append actions alongside validation feedback.